package com.spring;

import com.spring.annotation.Autowired;
import com.spring.annotation.Component;
import com.spring.annotation.ComponentScan;
import com.spring.annotation.Scope;
import com.spring.annotation.Aspect;
import com.spring.annotation.Before;
import com.spring.annotation.After;
import com.spring.domain.BeanDefinition;
import com.spring.bean.BeanNameAware;
import com.spring.bean.BeanPostProcessor;
import com.spring.bean.InitializingBean;
import com.spring.domain.AspectInfo;
import com.spring.aop.PointcutParser;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class XiejunApplicationContext {

    private Class configClass;
    private ConcurrentHashMap<String, Object> singletonObjects = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();
    // 存储切面信息
    private List<AspectInfo> aspectInfos = new ArrayList<>();

    public XiejunApplicationContext(Class configClass) throws Exception {
        this.configClass = configClass;
        //解析配置类
        //解析ComponentScan---->扫描路径---->扫描---->得到BeanDefinition---->放入beanDefinitionMap
        scan(configClass);
        for (Map.Entry<String, BeanDefinition> entry : beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();
            System.out.println("创建bean成功: " + beanName);
            if (beanDefinition.getScope().equals("singleton")) {
                Object bean = createBean(beanName,beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        }
    }

    public Object createBean(String beanName,BeanDefinition beanDefinition) throws Exception {
        Class clazz = beanDefinition.getClazz();
        Object instance = clazz.getDeclaredConstructor().newInstance(); //无参构造器
        //依赖注入，对属性进行赋值
        for (Field declaredField : clazz.getDeclaredFields()) {
            if (declaredField.isAnnotationPresent(Autowired.class)) {
                Object bean = getBean(declaredField.getName());
                declaredField.setAccessible(true);
                declaredField.set(instance, bean);
            }
        }
        //Aware回调,给Bean注入名字
        if (instance instanceof BeanNameAware){
            ((BeanNameAware)instance).setBeanName(beanName);
        }

        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            //对instance进行处理，比如说AOP
            instance = beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
        }

        //初始化回调，所有Bean的初始化操作。Spring会调用这个方法，这个方法做了什么你可以在UsrService中自己写。
        if (instance instanceof InitializingBean) {
            ((InitializingBean) instance).afterPropertiesSet();
        }

        for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
            instance = beanPostProcessor.postProcessAfterInitialization(instance, beanName);
        }

        return instance;
    }

    private void scan(Class configClass) {
        ComponentScan componentScanAnnotation = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
        //扫描路径
        String path = componentScanAnnotation.value(); //com.xiejun
        path = path.replace(".", "/");
        //扫描,通过类加载器来得到包下的类
        //APP-->classpath---->
        ClassLoader classLoader = XiejunApplicationContext.class.getClassLoader();
        URL resource = classLoader.getResource(path);
        File file = new File(resource.getFile());
        scanRecursively(file, classLoader, path); // 使用递归扫描
    }

    // 添加递归扫描方法
    private void scanRecursively(File file, ClassLoader classLoader, String basePath) {
        if (file.isDirectory()) {
            File[] files = file.listFiles();
            if (files != null) {
                for (File f : files) {
                    scanRecursively(f, classLoader, basePath); // 递归扫描子目录
                }
            }
        } else if (file.getName().endsWith(".class")) {
            String fileName = file.getAbsolutePath();
            // 处理Windows和Unix系统路径分隔符差异
            String className = fileName.substring(fileName.indexOf(basePath.replace("/", File.separator)), fileName.indexOf(".class"));
            className = className.replace(File.separator, ".");
            try {
                Class<?> clazz = classLoader.loadClass(className);
                if (clazz.isAnnotationPresent(Component.class)) {
                    //表示当前的这个类是一个Bean
                    //先判断是单例还是多例
                    //BeanDefinition
                    //判断这个class对象是不是实现了这个接口
                    if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                        BeanPostProcessor instance = (BeanPostProcessor) clazz.getDeclaredConstructor().newInstance();
                        beanPostProcessorList.add(instance);
                    }
                    
                    // 检查是否是切面类
                    if (clazz.isAnnotationPresent(Aspect.class)) {
                        // 解析切面类中的通知方法
                        parseAspect(clazz);
                    }

                    Component componentAnnotation = clazz.getAnnotation(Component.class);
                    String beanName = componentAnnotation.value();
                    // 如果没有指定bean名称，则使用类名首字母小写作为bean名称
                    if (beanName == null || beanName.isEmpty()) {
                        String simpleName = clazz.getSimpleName();
                        beanName = Character.toLowerCase(simpleName.charAt(0)) + simpleName.substring(1);
                    }
                    
                    BeanDefinition beanDefinition = new BeanDefinition();
                    beanDefinition.setClazz(clazz);
                    if (clazz.isAnnotationPresent(Scope.class)){
                        Scope scopeAnnotation = clazz.getDeclaredAnnotation(Scope.class);
                        beanDefinition.setScope(scopeAnnotation.value());
                    }else{
                        beanDefinition.setScope("singleton");
                    }
                    beanDefinitionMap.put(beanName, beanDefinition);
                }
            } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException |
                     IllegalAccessException | InvocationTargetException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    // 解析切面类
    private void parseAspect(Class<?> clazz) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Object aspectInstance = clazz.getDeclaredConstructor().newInstance();
        
        // 遍历所有方法，查找通知注解
        Method[] methods = clazz.getDeclaredMethods();
        for (Method method : methods) {
            if (method.isAnnotationPresent(Before.class)) {
                Before before = method.getAnnotation(Before.class);
                AspectInfo aspectInfo = new AspectInfo(aspectInstance, method, before.value(), 1);
                aspectInfos.add(aspectInfo);
            } else if (method.isAnnotationPresent(After.class)) {
                After after = method.getAnnotation(After.class);
                AspectInfo aspectInfo = new AspectInfo(aspectInstance, method, after.value(), 2);
                aspectInfos.add(aspectInfo);
            }
        }
    }

    public Object getBean(String beanName) throws Exception {
        if (beanDefinitionMap.containsKey(beanName)) {
            BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
            if(beanDefinition.getScope().equals("singleton")){
                Object o = singletonObjects.get(beanName);
                // 创建代理对象（如果需要）
                return createProxyIfNecessary(o, beanName, beanDefinition.getClazz());
            } else {
                //创建bean对象
                Object bean = createBean(beanName, beanDefinition);
                // 创建代理对象（如果需要）
                return createProxyIfNecessary(bean, beanName, beanDefinition.getClazz());
            }
        } else {
            //不存在对应的Bean
            throw new NullPointerException();
        }
    }
    
    // 如果需要，创建代理对象
    private Object createProxyIfNecessary(Object target, String beanName, Class<?> targetClass) {
        // 检查是否有匹配的切面
        List<AspectInfo> matchedAspects = new ArrayList<>();
        for (AspectInfo aspectInfo : aspectInfos) {
            // 检查切点表达式是否匹配
            // 这里简化处理，实际应该更精确地匹配切点表达式
            // 我们检查目标类是否在切点表达式指定的包中
            String expression = aspectInfo.getPointcutExpression();
            if (PointcutParser.matches(expression, targetClass, null)) {
                matchedAspects.add(aspectInfo);
            }
        }
        
        // 如果有匹配的切面，则创建代理
        if (!matchedAspects.isEmpty()) {
            System.out.println("为 " + beanName + " 创建代理对象");
            return Proxy.newProxyInstance(
                targetClass.getClassLoader(),
                targetClass.getInterfaces(),
                (proxy, method, args) -> {
                    // 执行Before通知
                    for (AspectInfo aspectInfo : matchedAspects) {
                        if (aspectInfo.getAdviceType() == 1) { // Before advice
                            System.out.println("执行Before通知");
                            aspectInfo.getAdviceMethod().invoke(aspectInfo.getAspectInstance());
                        }
                    }
                    
                    Object result;
                    try {
                        result = method.invoke(target, args);
                    } catch (Exception e) {
                        throw e;
                    }
                    
                    // 执行After通知
                    for (AspectInfo aspectInfo : matchedAspects) {
                        if (aspectInfo.getAdviceType() == 2) { // After advice
                            System.out.println("执行After通知");
                            aspectInfo.getAdviceMethod().invoke(aspectInfo.getAspectInstance());
                        }
                    }
                    
                    return result;
                }
            );
        }
        
        return target;
    }
}